#ifndef cathlibcpp_exception_H
#define cathlibcpp_exception_H

// File:       exception.h
// Author:     (c) Miles Sabin, 1996
// Purpose:    fake exception handling for Acorn CFront


#ifndef cathlibcpp_bool_H
#include "bool.h"
#endif

#ifndef cathlibcpp_config_H
#include "config.h"
#endif

#ifndef cathlibcpp_ptypeinfo_H
#include "ptypeinfo.h"
#endif


// exception

class exception
{
  public:

    // constructors
    exception();
    virtual ~exception();

    // accessors
    virtual char const* what() const;

    RTTI_SCAFFOLDING_DECL
};


// terminate handler

typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler);
void terminate();


// uncaught_exception

extern bool uncaught_exception();


#ifdef BUILTIN_EXCEPTION

#define DESTROY_ON_THROW

#define throw(ex)                throw ex
#define rethrow                  throw

#define catch(T, id)             catch(T& id)
#define catch_DOTS               catch(...)

#define BEGIN_HANDLERS
#define END_HANDLERS

#else // !BUILTIN_EXCEPTION

#ifndef included_setjmp_H
#define included_setjmp_H
#include <setjmp.h>               // for jmp_buf, setjmp
#endif


// ExceptionContext

struct ExceptionState;

class ExceptionContext
{
  public:

    // constructors
    ExceptionContext();
    ~ExceptionContext();

    // accessors
    bool is_uncaught() const;
    exception const* get_exception() const;

    // mutators
    void set_caught();
    void rethrow_exception();
    exception const* release_exception();

    int* get_jmp_buf();

    static void throw_exception(exception const*, exception const*);

    static bool uncaught_exception();

    static ExceptionState* get_current_state();

    typedef ExceptionState* (*exception_state_accessor_t)();
    static void set_current_state_accessor(exception_state_accessor_t accessor);

  private:

    void delete_pending_exceptions();

    bool uncaught_;
    exception const* exception_;
    jmp_buf jmp_buf_;
    ExceptionContext* prev_;
    ExceptionContext* next_;
};


// Implementation of ExceptionContext

inline bool ExceptionContext::is_uncaught() const
  { return uncaught_; }

inline exception const* ExceptionContext::get_exception() const
  { return exception_; }

inline void ExceptionContext::set_caught()
  { uncaught_ = false; }

inline int* ExceptionContext::get_jmp_buf()
  { return jmp_buf_; }


// macros for faking exception syntax


// fake a try statement ...

#define try                                                                                            \
  {                                                                                                    \
    ExceptionContext __current_context;                                                                \
    if(setjmp(__current_context.get_jmp_buf()) == 0)

#define BEGIN_HANDLERS                                                                                 \
    else                                                                                               \
    {                                                                                                  \
      {                                                                                                \
        {


// fake a catch statement ...

#define catch(T, id)                                                                                   \
        }                                                                                              \
      }                                                                                                \
      if(__current_context.is_uncaught())                                                              \
      {                                                                                                \
        T* id ## _ptr_ = PTR_dynamic_cast(T, __current_context.get_exception());                       \
        if(id ## _ptr_ != 0)                                                                           \
        {                                                                                              \
          T& id = *id ## _ptr_;                                                                        \
          __current_context.set_caught();

#define catch_DOTS                                                                                     \
        }                                                                                              \
      }                                                                                                \
      if(__current_context.is_uncaught())                                                              \
      {                                                                                                \
        {                                                                                              \
          __current_context.set_caught();

#define END_HANDLERS                                                                                   \
        }                                                                                              \
      }                                                                                                \
      if(__current_context.is_uncaught())                                                              \
        __current_context.rethrow_exception();                                                         \
    }                                                                                                  \
  }


// fake a 'throw ...;'

#define throw(ex) throw_exception(ex)

template<class T>
inline void throw_exception(T const& r)
{
  ExceptionContext::throw_exception(&r, new T(r));
}


// fake a 'throw;'

#define rethrow                                                                                        \
    __current_context.rethrow_exception()

#define __tempname(nm)         __tempname2(nm, __LINE__)
#define __tempname2(nm, line)  __tempname3(nm, line)
#define __tempname3(nm, line)  nm ## line

#define DESTROY_ON_THROW(o)  AutoDestroyer_base const& __tempname(__ex) = make_auto_destroyer(o);


class AutoDestroyer_base
{
  friend class ExceptionContext;

  public:

    // constructors
    AutoDestroyer_base();
    ~AutoDestroyer_base();                    // destructor intentionally non-virtual

  private:

    // mutators
    virtual void destroy() const = 0;

    static void unwind(ExceptionContext* c);

    ExceptionContext* context_;
    AutoDestroyer_base* prev_;
};


inline void destroy(AutoDestroyer_base**)
{}


template<class T>
class AutoDestroyer : public AutoDestroyer_base
{
  public:

    // constructors
    AutoDestroyer(T& target);

  private:

    // mutators
    void destroy() const;

    T& target_;
};

template<class T>
inline AutoDestroyer<T> make_auto_destroyer(T& t)
{
  return AutoDestroyer<T>(t);
}

struct ExceptionState
{
  ExceptionState();

  ExceptionContext* current_context;
  exception const* current_exception;
  AutoDestroyer_base* current_destroyer;
};

#endif // BUILTIN_EXCEPTION

#endif
